Para acceder a la Interactive Python Notebook (ipynb) correspondiente a esta página HTML, haga click en el siguiente link.
Esta notebook contiene algunas modificaciones surgidas a partir de la presentación del día 30/5/2024.
Para acceder a la versión anterior de esta notebook, utilizada el día de la presentación, hacer click en este link.
Para acceder a la información descriptiva de los cambios realizados entre ambas versiones, hacer click en este link.
Comenzamos con un análisis básico del dataset que se encuentra en el siguiente link.
Cargamos las librerías y hacemos las configuraciones que vamos a necesitar:
# IMPORTANTE!!!
# Si desea ejecutar esta notebook en forma local, puede instalar todo lo necesario con:
# pip install notebook pandas altair folium vegafusion-python-embed vegafusion vl-convert-python
# Si desea ejecutar esta notebook desde Github Codespaces, puede instalar todo lo necesario con:
# pip install altair folium vegafusion-python-embed vegafusion vl-convert-python
import pandas as pd
import altair as alt
import folium
from folium.plugins import HeatMap
import glob
import os
alt.data_transformers.enable("vegafusion")
DataTransformerRegistry.enable('vegafusion')
Limpieza inicial de datos¶
Abrimos el archivo y observamos los datos:
df = pd.read_csv('listings.csv', low_memory = False)
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 36561 entries, 0 to 36560 Data columns (total 18 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 36561 non-null int64 1 name 36561 non-null object 2 host_id 36561 non-null int64 3 host_name 36561 non-null object 4 neighbourhood_group 0 non-null float64 5 neighbourhood 36561 non-null object 6 latitude 36561 non-null float64 7 longitude 36561 non-null float64 8 room_type 36561 non-null object 9 price 33911 non-null float64 10 minimum_nights 36561 non-null int64 11 number_of_reviews 36561 non-null int64 12 last_review 29679 non-null object 13 reviews_per_month 29679 non-null float64 14 calculated_host_listings_count 36561 non-null int64 15 availability_365 36561 non-null int64 16 number_of_reviews_ltm 36561 non-null int64 17 license 440 non-null object dtypes: float64(5), int64(7), object(6) memory usage: 5.0+ MB
df.describe()
| id | host_id | neighbourhood_group | latitude | longitude | price | minimum_nights | number_of_reviews | reviews_per_month | calculated_host_listings_count | availability_365 | number_of_reviews_ltm | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 3.656100e+04 | 3.656100e+04 | 0.0 | 36561.000000 | 36561.000000 | 3.391100e+04 | 36561.000000 | 36561.000000 | 29679.000000 | 36561.000000 | 36561.000000 | 36561.000000 |
| mean | 6.325924e+17 | 2.032641e+08 | NaN | -34.591300 | -58.417899 | 5.954551e+04 | 5.605454 | 23.255190 | 1.455364 | 18.978830 | 215.180794 | 10.225158 |
| std | 4.442096e+17 | 1.929296e+08 | NaN | 0.018490 | 0.030271 | 7.015697e+05 | 23.071708 | 39.888967 | 1.409139 | 43.477847 | 117.459621 | 14.629388 |
| min | 1.150800e+04 | 1.342600e+04 | NaN | -34.693700 | -58.530890 | 2.600000e+02 | 1.000000 | 0.000000 | 0.010000 | 1.000000 | 0.000000 | 0.000000 |
| 25% | 4.289224e+07 | 2.807387e+07 | NaN | -34.602477 | -58.437889 | 2.273000e+04 | 1.000000 | 1.000000 | 0.430000 | 1.000000 | 113.000000 | 0.000000 |
| 50% | 8.212314e+17 | 1.339204e+08 | NaN | -34.590704 | -58.419440 | 3.059800e+04 | 2.000000 | 8.000000 | 1.040000 | 2.000000 | 241.000000 | 4.000000 |
| 75% | 1.007852e+18 | 3.947047e+08 | NaN | -34.580730 | -58.392992 | 4.371200e+04 | 4.000000 | 28.000000 | 2.090000 | 13.000000 | 331.000000 | 15.000000 |
| max | 1.144128e+18 | 5.744205e+08 | NaN | -34.534980 | -58.355403 | 4.371150e+07 | 1000.000000 | 813.000000 | 53.470000 | 294.000000 | 365.000000 | 608.000000 |
Hacemos una primera limpieza de datos:
len(df)
36561
# Se eliminan los registros que no tienen precio.
df = df.dropna(subset=['price'])
len(df)
33911
# Se elimina esta columna, que para el caso de CABA no contiene datos
df = df.drop(columns=['neighbourhood_group'])
Algunas visualizaciones preliminares de los datos¶
Boxplot de los precios:
# Creamos un boxplot para la columna 'price'
boxplot = alt.Chart(df).mark_boxplot().encode(
x='price'
).properties(
title='Boxplot de precios'
)
boxplot = boxplot.properties(width=1024, height=100)
boxplot.show()
Se ven varios valores ridículos que claramente son errores de carga o algún problema similar. Creamos un dataframe alternativo eliminando los outliers. Lo llamamos df_limpio. Lo usaremos en los gráficos en los que corresponda de acuerdo al análisis que estemos haciendo.
# Cálculos de cuartiles y distancia intercuartil
Q1 = df['price'].quantile(0.25)
Q3 = df['price'].quantile(0.75)
IQR = Q3 - Q1
# Calculamos los límites para los outliers
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# Calculamos el nuevo dataframe sin los outliers
df_limpio = df[(df['price'] >= lower_bound) & (df['price'] <= upper_bound)]
Graficamos nuevamente el boxplot, ahora sin los outliers:
# Creamos un boxplot para la columna 'price'
boxplot = alt.Chart(df_limpio).mark_boxplot().encode(
x='price'
).properties(
title='Boxplot de precios (sin outliers)'
)
boxplot = boxplot.properties(width=1024, height=100)
boxplot.show()
Hacemos un histograma de precios:
# Creamos un histograma para la columna 'price'
limite = 150000
bins = 25
histogram = alt.Chart(df[df['price'] <= limite]).mark_bar().encode(
alt.X('price', bin=alt.Bin(maxbins=bins), title='Precio'),
alt.Y('count()', title='Cantidad', axis=alt.Axis(title=None, ticks=False, labels=False)),
color='room_type:N'
).properties(
title='Histograma de precios'
)
histogram = histogram.properties(width=950)
histogram.show()
Datos de disponibilidad:
histogram = alt.Chart(df[df['price'] <= limite]).mark_bar().encode(
alt.X('availability_365', bin=alt.Bin(maxbins=bins), title='Días de disponibilidad'),
alt.Y('count()', title='Cantidad', axis=alt.Axis(title=None, ticks=False, labels=False))
).properties(
title='Histograma de disponibilidad'
)
histogram = histogram.properties(width=1024)
histogram.show()
Tiramos preliminarmente los datos de coordenadas haciendo un scatter-plot, para ver que sean coherentes:
# Creamos scatter-plot a partir de las latitudes y longitudes
map_chart = alt.Chart(df).mark_circle().encode(
longitude='longitude:Q',
latitude='latitude:Q',
size=alt.value(5)
).properties(
title='Scatter-plot de latitudes y longitudes'
)
map_chart = map_chart.properties(width=1024, height=1024)
map_chart.show()